package cn.itcast.nems.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import feign.FeignException;
import feign.RequestTemplate;
import feign.Response;
import feign.Util;
import feign.codec.EncodeException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Objects;
import java.util.Set;

import static java.nio.charset.StandardCharsets.UTF_8;

public class FeignClientEncoder extends SpringEncoder {

    public FeignClientEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        super(messageConverters);
    }

    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {

        try {
            super.encode(object, bodyType, template);
            final String s = JSON.toJSONString(object);
            final Object jsonObject = JSON.parse(s);
            convertToCamelCase(jsonObject);
            template.body(JSON.toJSONString(jsonObject));
        } catch (Exception ex) {

        }

        System.out.println();
    }

    public static class FeignClientDecoder extends SpringDecoder {
        public FeignClientDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
            super(messageConverters);
        }

        @Override
        public Object decode(Response response, Type type) throws IOException, FeignException {
            final Response.Body body = response.body();
            final String bodyClassName = body.getClass().getName();
            //Response.ByteArrayBody feign.Response$InputStreamBody feign.Response$ByteArrayBody
            //因为ByteArrayBody是private 所以不能instance of
            if (Objects.equals(bodyClassName, "feign.Response$ByteArrayBody") || Objects.equals(bodyClassName, "feign.Response$InputStreamBody")) {
                String result;
                if (Objects.equals(bodyClassName, "feign.Response$ByteArrayBody")) {
                    result = body.toString();
                } else {
                    byte[] bodyData = Util.toByteArray(body.asInputStream());
                    result = Util.decodeOrDefault(bodyData, UTF_8, "Binary data");
                }
                final Response.Builder respBuild = Response.builder()
                        .headers(response.headers())
                        .reason(response.reason())
                        .status(response.status())
                        .request(response.request());

                if (StringUtils.isNoneBlank(result) && JSONUtil.isJson(result)) {
                    final Object jsonObject = JSON.parse(result);
                    convertToUnderline(jsonObject);
                    respBuild.body(JSON.toJSONString(jsonObject), Charset.defaultCharset());
                } else {
                    respBuild.body(result, Charset.defaultCharset());
                }
                return super.decode(respBuild.build(), type);
            }
            return super.decode(response, type);
        }
    }

    public final static void convertToUnderline(Object json) {
        if (json instanceof JSONArray) {
            JSONArray arr = (JSONArray) json;
            for (Object obj : arr) {
                convertToUnderline(obj);
            }
        } else if (json instanceof JSONObject) {
            JSONObject jo = (JSONObject) json;
            Set<String> keys = jo.keySet();
            String[] array = keys.toArray(new String[keys.size()]);
            for (String key : array) {
                Object value = jo.get(key);
                final String underLineKey = StrUtil.toUnderlineCase(key);
                jo.remove(key);
                jo.put(underLineKey, value);
                convertToUnderline(value);
            }
        }
    }

    public final static void convertToCamelCase(Object json) {
        if (json instanceof JSONArray) {
            JSONArray arr = (JSONArray) json;
            for (Object obj : arr) {
                convertToCamelCase(obj);
            }
        } else if (json instanceof JSONObject) {
            JSONObject jo = (JSONObject) json;
            Set<String> keys = jo.keySet();
            String[] array = keys.toArray(new String[keys.size()]);
            for (String key : array) {
                Object value = jo.get(key);
                final String underLineKey = StrUtil.toCamelCase(key);
                jo.remove(key);
                jo.put(underLineKey, value);
                convertToCamelCase(value);
            }
        }
    }

}

